//
// Created by abstergo on 24-10-30.
//

#include <math/math.h>

using namespace tbs::math;

Decimal::Decimal(double v) : Decimal(std::to_string(v)) {
}

Decimal::Decimal(float v) : Decimal(std::to_string(v)) {
}

Decimal::Decimal(int v) : Decimal(std::to_string(v)) {
}

Decimal::Decimal(long v) : Decimal(std::to_string(v)) {
}

Decimal::Decimal(unsigned long v) : Decimal(std::to_string(v)) {
}

Decimal::Decimal(unsigned int v) : Decimal(std::to_string(v)) {
}

Decimal::Decimal(const Decimal &v)
    : positiveAndNegativeMarkings(v.positiveAndNegativeMarkings),
      _integers(v._integers),
      _scale(v._scale),
      _context(v._context) {
}

void Decimal::initByStr(Decimal &obj, CONST char *v) {
  if (obj._context == nullptr) {
    obj._context = TenMathContext;
  }
  // 检查输入字符串是否为空
  if (v == nullptr || *v == '\0') {
    throw std::invalid_argument("Empty input string");
  }
  obj._scale = 0;
  char cv;
  bool hasDecimal = false;
  int sign = MathContext::SIGN_NUMBER;
  bool isFirst = true;
  while (*v != '\0') {
    cv = obj._context->readChar(*v, sign);
    if (isFirst) {
      if (sign == MathContext::SIGN_ERROR || sign == MathContext::SIGN_DECIMAL) {
        throw std::invalid_argument("错误的头部字符");
      } else if (sign == MathContext::SIGN_NEGATIVE) {
        obj.toNegative();
      } else if (sign == MathContext::SIGN_POSITIVE) {
        obj.toPositive();
      }
      isFirst = false;
      if (sign == MathContext::SIGN_NUMBER) {
        continue;
      }
    } else {
      switch (sign) {
        case MathContext::SIGN_NUMBER: obj._integers.push_back(cv);
          if (!hasDecimal) {
            obj._scale++;
          }
          break;
        case MathContext::SIGN_DECIMAL:
          if (hasDecimal)
            throw std::invalid_argument("多个小数点");
          else {
            hasDecimal = true;
          }
          break;
        default: throw std::invalid_argument("Invalid input string");
      }
    }
    v++;
  }
}

Decimal::Decimal(tbs::str_type v) : Decimal(v.c_str()) {
}

Decimal::Decimal(const char *v, MathContext *context) : _context(context) {
  Decimal::initByStr(*this, v);
}

int Decimal::seekBitFromIntegers(CONST int &index) const {
  return index < scale() ? _integers[scale() - 1 - index] : 0;
}

int Decimal::seekBitFromDecimals(CONST int &index) const {
  return index < accuracy() ? _integers[scale() + index] : 0;
}

void Decimal::setBitFromIntegers(const int &index, const char &v) {
  if (index >= scale()) {
    scale(index + 1);
  }
  _integers[scale() - 1 - index] = v;

}

void Decimal::setBitFromDecimals(const int &index, const char &v) {
  if (index >= accuracy()) {
    accuracy(index + 1);
  }
  _integers[scale() + index] = v;

}

bool Decimal::isNegative() const {
  return positiveAndNegativeMarkings == MathContext::SIGN_NEGATIVE;
}

void Decimal::toPositive() {
  positiveAndNegativeMarkings = MathContext::SIGN_POSITIVE;
}

void Decimal::toNegative() {
  positiveAndNegativeMarkings = MathContext::SIGN_NEGATIVE;
}

void Decimal::reverse() {
  positiveAndNegativeMarkings = isNegative() ? MathContext::SIGN_POSITIVE : MathContext::SIGN_NEGATIVE;
}

void Decimal::accuracy(int v) {
  if (v < 0) {
    return;
  }
  long cp = accuracy() - v;
  if (cp == 0) {
    return;
  }
  if (cp > 0) {
    _integers.erase(_integers.cend() - cp, _integers.cend());
  } else {
    _integers.insert(_integers.cend(), -cp, 0);
  }
}

int Decimal::accuracy() const {
  return _integers.size() - _scale;
}

int Decimal::scale() const {
  return _scale;
}

void Decimal::scale(int v) {
  if (v < 0) {
    return;
  }
  int cp = _scale - v;
  if (cp == 0) {
    return;
  }
  if (cp > 0) {
    _integers.erase(_integers.cbegin(), _integers.cbegin() + cp);
  } else {
    _integers.insert(_integers.cbegin(), -cp, 0);
  }
  _scale = v;
}

int Decimal::compare(CONST Decimal &a, CONST Decimal &b) {
  int aN = a.isNegative(), bN = b.isNegative();
  return absCompare(a, b) * aN;
}

int Decimal::absCompare(CONST Decimal &a, CONST Decimal &b) {
  int asc = a.availableScale();
  int bsc = b.availableScale();
  if (asc > bsc) {
    return 1;
  } else if (bsc > asc) {
    return -1;
  }

  auto sc = std::max(asc, bsc);
  int ao = a.scale() - asc;
  int bo = b.scale() - bsc;
  for (int i = 0; i < sc; i++) {
    auto va = a._integers[ao + i];
    auto vb = b._integers[bo + i];
    if (va == vb)
      continue;
    return va > vb ? 1 : -1;
  }
  asc = a.avaliableAccuracy();
  bsc = b.avaliableAccuracy();
  sc = std::min(asc, bsc);
  int ap = a.scale();
  int bp = b.scale();
  for (auto i = 0; i < sc; i++) {
    auto va = a._integers[ap + i];
    auto vb = b._integers[bp + i];
    if (va == vb)
      continue;
    return va > vb ? 1 : -1;
  }
  if (asc == bsc) {
    return 0;
  } else
    return asc > bsc ? 1 : -1;
}

bool Decimal::operator==(const Decimal &v) const {
  return compare(*this, v) == 0;
}

bool Decimal::operator<(const Decimal &v) const {
  return compare(*this, v) < 0;
}

bool Decimal::operator>(const Decimal &v) const {
  return compare(*this, v) > 0;
}

Decimal &Decimal::operator=(const Decimal &o) {
  this->positiveAndNegativeMarkings = o.positiveAndNegativeMarkings;
  this->_integers = o._integers;
  this->_scale = o._scale;
  this->_context = o._context;
  return *this;
}

Decimal &Decimal::operator=(const float &v) {
  initByStr(*this, std::to_string(v).c_str());
  return *this;
}

Decimal &Decimal::operator=(const double &v) {
  initByStr(*this, std::to_string(v).c_str());
  return *this;
}

Decimal &Decimal::operator=(const long &v) {
  initByStr(*this, std::to_string(v).c_str());
  return *this;
}

Decimal &Decimal::operator=(const unsigned long &v) {
  initByStr(*this, std::to_string(v).c_str());
  return *this;
}

Decimal &Decimal::operator=(const unsigned int &v) {
  initByStr(*this, std::to_string(v).c_str());
  return *this;
}

Decimal &Decimal::operator=(const tbs::str_type &v) {
  initByStr(*this, v.c_str());
  return *this;
}

tbs::str_type Decimal::toString() const {
  tbs::str_type result = isNegative() ? "-" : "";
  if (availableScale() > 0) {
    for (int i = scale() - 1; i >= 0; i--) {
      result += _context->readByte(seekBitFromIntegers(i));
    }
  } else {
    result += "0";
  }

  if (avaliableAccuracy() > 0) {
    result += ".";
    for (int i = 0; i < accuracy(); i++) {
      result += _context->readByte(seekBitFromDecimals(i));
    }
  }
  return result;
}

Decimal Decimal::toInteger(const Decimal &obj) {
  Decimal r(obj);
  r.accuracy(0);
  return r;
}
void Decimal::moveDecimal(int v) {
  //v是变化值
  if (v < 0) {
    int cp = scale() + v;
    if (cp >= 0) {
      _scale = cp;
    } else {
      _scale = 0;
      _integers.insert(_integers.cbegin(), -cp, 0);
    }
  } else if (v > 0) {
    int cp = scale() + v;
    if (cp >= _integers.size()) {
      _integers.insert(_integers.cend(), cp - _integers.size(), 0);
      _scale = _integers.size();
    } else {
      _scale += v;
    }

  } else {
    return;
  }
}
Decimal Decimal::decimalAsInteger(const Decimal &obj) {
  Decimal re(obj);
  re.scale(0);
  re.moveDecimal(re.avaliableAccuracy());
  return re;
}
int Decimal::avaliableAccuracy() const {
  int acc = accuracy();
  for (int i = 0; i < acc; i++) {
    if (_integers[_integers.size() - 1 - i] != 0) {
      return acc - i;
    }
  }
  return 0;
}
int Decimal::availableScale() const {
  for (int i = 0; i < scale(); i++) {
    if (_integers[i] != 0) {
      return scale() - i;
    }
  }
  return 0;
}

void Decimal::operateInAvaliable(op_func f, const Decimal::PartOfDecimal &part) {
  int start, end;
  int j = 0;
  switch (part) {
    case Decimal::PartOfDecimal::INTEGER: start = scale() - 1;
      end = (scale() - availableScale());
      for (int i = start; i >= end; i--) {
        f(_integers[i], j++);
      }
      break;
    case Decimal::PartOfDecimal::DECIMAL: start = scale();
      end = scale() + avaliableAccuracy();
      j = avaliableAccuracy() - 1;
      for (int i = end - 1; i >= start; i--) {
        f(_integers[i], j--);
      }
      break;
  }

}
void Decimal::operateAll(Decimal::op_func f, const Decimal::PartOfDecimal &part) {
  int start, end;
  int j = 0;
  switch (part) {
    case Decimal::PartOfDecimal::INTEGER: start = scale() - 1;
      end = 0;
      for (int i = start; i >= end; i--) {
        if (!f(_integers[i], j++))
          break;
      }
      break;
    case Decimal::PartOfDecimal::DECIMAL: start = scale();
      end = scale() + accuracy();
      j = accuracy() - 1;
      for (int i = end - 1; i >= start; i--) {
        if (!f(_integers[i], j--))
          break;
      }
      break;
  }

}
void Decimal::operateFromIntegersAt(const int &index, Decimal::op_func func) {
  if (index >= scale()) {
    scale(index + 1);
  }
  func(_integers[scale() - 1 - index], index);

}
void Decimal::operateFromDecimalsAt(const int &index, Decimal::op_func func) {
  if (index >= accuracy()) {
    accuracy(index + 1);
  }
  func(_integers[scale() + index], index);

}

void Decimal::seekFromDecimal(Decimal::seek_func sf, const bool &isAll) CONST {
  int ed = isAll ? accuracy() : avaliableAccuracy();
  for (int i = ed - 1; i >= scale(); i--) {
    if (!sf(_integers[i], i - scale()))
      break;
  }
}
void Decimal::seekFromInteger(Decimal::seek_func sf, const bool &isAll) CONST {
  int ed = scale() - 1;
  int beg = isAll ? 0 : scale() - availableScale();
  for (int i = ed; i >= beg; i--) {
    if (!sf(_integers[i], ed - i))
      break;
  }
}
void Decimal::magicallyMoveDecimal(const int &v, Decimal::magic_func fc) CONST {
  int old = _scale;
  if (v < 0) {
    int cp = scale() + v;
    if (cp >= 0) {
      _scale = cp;
    } else {
      throw std::invalid_argument("Decimal::magicallyMoveDecimal can not expand too much.");
    }
    fc(*this);
  } else if (v > 0) {
    int cp = scale() + v;
    if (cp > _integers.size()) {
      throw std::invalid_argument("Decimal::magicallyMoveDecimal can not expand too much.");
    } else {
      _scale += v;
    }
    fc(*this);
  } else {
    fc(*this);
  }
  _scale = old;
}
Decimal::Decimal(Decimal &&o) noexcept
    : _scale(o._scale),
      _integers(std::move(o._integers)),
      positiveAndNegativeMarkings(o.positiveAndNegativeMarkings),
      _context(o._context) {

}
Decimal &Decimal::operator=(Decimal &&o) noexcept {

  _integers = std::move(o._integers);
  _scale = o._scale;
  positiveAndNegativeMarkings = o.positiveAndNegativeMarkings;
  _context = o._context;
  return *this;
}


